package Infinite::Sequence;
use strict;
use warnings;
use diagnostics;
use overload 
        '+' => sub { overload_operator( '+', @_ ) },
        '-' => sub { overload_operator( '-', @_ ) },
        '*' => sub { overload_operator( '*', @_ ) },
        '/' => sub { overload_operator( '/', @_ ) },
        '%' => sub { overload_operator( '%', @_ ) },
        '**' => sub { overload_operator( '**', @_ ) }
;
use Infinite::Sequence::Pattern;
use Infinite::Sequence::Combined;
use Infinite::Sequence::Odd;
use Infinite::Sequence::Fibonacci;
use Infinite::Sequence::Prime;
use Infinite::Sequence::Factorial;
use Infinite::Sequence::Sum qw{SUM Sum};
use Infinite::Sequence::Product qw{PRODUCT Product};
use Infinite::Sequence::Set qw{SET};
use Infinite::Sequence::RandomRange;
use Exporter qw{import};
our @EXPORT_OK = qw{
	take show say
	SUM PRODUCT SET
	Naturals Even Odd Fibonacci Primes Factorials
	Pattern RandomRange
};
our %EXPORT_TAGS = (
	all => \@EXPORT_OK,
);

sub Naturals()		{ new Infinite::Sequence::Natural }
sub Even()			{ new Infinite::Sequence::Even }
sub Odd()			{ new Infinite::Sequence::Odd }
sub Fibonacci()		{ new Infinite::Sequence::Fibonacci }
sub Primes()		{ new Infinite::Sequence::Prime }
sub Factorials()	{ new Infinite::Sequence::Factorial }
sub Pattern(@)		{ new Infinite::Sequence::Pattern( @_ ) }
sub RandomRange(@)	{ new Infinite::Sequence::RandomRange( @_ ) }

sub overload_operator {
    my( $op, $A, $B, $swapped ) = @_;
    new Infinite::Sequence::Combined( $A, $B, $op, $swapped )
}

sub cast {
	my( $sequence_or_set ) = @_;
	$sequence_or_set->isa( q{Infinite::Sequence::Set} ) ?
    	$sequence_or_set->sequence :
    	$sequence_or_set
}

sub new { bless {}, shift }
sub increment { 1 }
sub increment_wrap { shift->increment }

sub say { print @_, qq{\n} }

sub take {
    my( $count, $sequence ) = @_;
    $sequence = cast $sequence;
    
    my @elements;
    push @elements, $sequence->increment_wrap() for 1 .. $count;
    @elements
}

sub show {
	my( @cut ) = @_;
	q{[ } . join( q{, }, @cut ) . q{ ]}
}

sub foreachelement {
    my( $this, $count, $do ) = @_;
    $do->( $this->increment ) for 1 .. $count
}

1